function Game() {
  
  this.width = 960;
  this.height = 600;
  
  this.interval;
  
  this.TIME_PER_FRAME = 33;
  this.frameCounter = 0;
  this.fps = 0;
  
  this.isPaused = false; // Pause that is caused by the user
  this.gamePause = false; // Pause that is caused by the game
  this.fader;
  
  this.levelNumber;  // Number of the level
  this.currentLevel; // reference to the current level object
  
  this.state;
  
  
  this.init = function() {
    this.drawEmpty();
    img.startLoading(); // Calls imageLoadingEnded when finished
  };
  
  
  this.imageLoadingEnded = function() {
    this.setupGame();
    this.startLoop();
    window.setInterval(function() {
      game.calculateFps();
    }, 1000);
  };
  
  
  this.setupGame = function() {
    
    this.state = Game.INGAME_STATE;// DEBUG: Game.INTRO_STATE;
    
    fonts.create("Hud", img.get("fontHud"), 1900, 20, 18, -1, 4);
    
    intro = new Intro();
    intro.init();
    
    hud = new Hud();
    hud.init();
    
    notifications = new Notifications();
    
    mapGenerator = new MapGenerator();
    mapGenerator.init();
    map = new Map();
    map.init(config.levels[1]);
    
    hero = new Hero();
    hero.init();
    

    // Key handlers
    this.isPaused = false;
    this.gamePause = false;

    keyboard.registerKeyUpHandler(Keyboard.P, function() {
      sound.play("button");
      game.togglePause();
    });

    
    this.fader = false;
    this.levelNumber = 1;
    this.currentLevel  = config.levels[this.levelNumber];
  };
  
  this.restart = function() {
    intro.init();
    hero.restart();
    
  };
  
  
  this.startLoop = function() {
    this.interval = window.setInterval(function() {
      game.loop();
    }, this.TIME_PER_FRAME);
  };
  
  
  this.loop = function() {
    this.update();
    this.draw();
    this.frameCounter++;
  };
  
  
  this.update = function() {
    
    if (!this.gamePause) {
      this.handleInput();
    }

    
    if (this.fader) {
      if(!this.fader.update()) {
        this.fader = false;
      }
    }
    
    // State dependent updates
    if (this.state == Game.INTRO_STATE) {
      intro.update();
    } 
    if(this.state ==  Game.INGAME_STATE) {
      
      if(!this.isPaused && !this.gamePause) {
        hero.update();
        notifications.update();
      }
      
      map.update();
      hud.update();
      
    }
    if (this.state == Game.OUTRO_STATE) {
      
    }
    if (this.state == Game.GAME_OVER) {
      
    }
    
  };
  
  
  this.draw = function() {
    this.drawEmpty();
        
    // DEBUG 
    if(true) {
      if(this.state == Game.INTRO_STATE) {
        intro.draw();
      }
      if(this.state == Game.INGAME_STATE) {
        map.draw();
        hud.draw();
        notifications.draw();
      }
      if(this.state == Game.OUTRO_STATE) {
        img.draw("winScreen",0,0);
      }
      if (this.state == Game.GAME_OVER) {
        img.draw("death", 0, 0); //TODO replace image
      }
      
      if(this.fader) {
        this.fader.draw();
      }
      
      if(this.isPaused) {
        img.draw("paused", 0, 0);
      }
      
    }
    else {
      map.drawDebug();
    }

  };
  
  
  this.calculateFps = function() {
    this.fps = this.frameCounter;
    this.frameCounter = 0;
    this.printFps();
  };
  
  
  this.printFps = function() {
    jQuery("#fps").html("FPS: " + this.fps);
  };
  
  
  this.drawEmpty = function() {
    c.fillStyle = "#000";
    c.fillRect(0, 0, this.width, this.height);
  };

  
  this.switchState = function(newState) {
    this.state = newState;
    if (this.state == Game.INGAME_STATE) {
      sound.play("inGame");
      notifications.show("level1");
    }
    if (this.state == Game.OUTRO_STATE) {
      sound.play("outroMusic");
    }
  };
  
  
  // Handles the input from the key board and the mouse (or touch screen)
  this.handleInput = function() {
      
    // Pauseble actions
    
    if(!this.isPaused) {  
      if (this.state == Game.INGAME_STATE) {
        var direction = { x : 0, y : 0 };
        if( hero.water <= 0 ) {
          return;
        }
      
        if (keyboard.isPressed(Keyboard.ARROW_RIGHT) || keyboard.isPressed(Keyboard.D)) {
          direction.x += 1;
        }
        if (keyboard.isPressed(Keyboard.ARROW_LEFT) || keyboard.isPressed(Keyboard.A)) {
          direction.x -= 1;
        }
    
        if (direction.x == 0) {
          if(keyboard.isPressed(Keyboard.ARROW_DOWN) || keyboard.isPressed(Keyboard.S)) {
            direction.y += 1;
          }
          if(keyboard.isPressed(Keyboard.ARROW_UP) || keyboard.isPressed(Keyboard.W)) {
            direction.y -= 1;
          }
        }
      
        if (keyboard.isPressed(Keyboard.SPACE_BAR)) {
          hero.action(direction);
          direction = { x : 0, y : 0 };
        } else {
          hero.resetWeaponTimer();
        
        }
        hero.move(direction);
      }
      else if (this.state == Game.GAME_OVER) {
        
        if (keyboard.isPressed(Keyboard.SPACE_BAR)) {
          game.switchState(Game.INTRO_STATE);
          game.restart();
        }
      }     
    }
  };
  
  
  this.completeLevel = function() {
    
    if (! this.currentLevel.checkCompleteConditions()) {
      return
    }
    if (this.levelNumber < config.NUM_LEVELS) {
      
      this.levelNumber += 1;
      this.currentLevel = config.levels[this.levelNumber]; 
      sound.play("levelComplete");     
      this.loadLevel(this.currentLevel);
      hero.food = hero.MAX_FOOD;
      hero.water = hero.MAX_WATER;
    }
    else {
      this.fader = this.getFader(130, function(){
        game.switchState(Game.OUTRO_STATE);
      });    
    }
  };
  
  // level = level object
  this.loadLevel = function(level) {
      this.startTransition( function(){
        map.init(level);
         hero.setToStartPosition();
          sound.play("inGame");
          notifications.show("level" + this.levelNumber);
        });
  };
  
  
  this.togglePause = function() {
    this.isPaused = !this.isPaused;
  };
  
  
  this.toggleGamePause = function() {
    this.gamePause = !this.gamePause;
  };
  
  
  
  this.startTransition = function(callback){
    this.TRANSITION_DURATION = 90; //TODO move
    this.fader = this.getFader(this.TRANSITION_DURATION, callback);
  };
  // duration: duratin of fading process in ticks, calback: method that is called in the middle of duration, TODO: use fade out and fade in duration
  this.getFader = function(_duration, _callback) {
    
    return new function() {
      
      this.duration = _duration;
      this.ticksSinceStart = 0;
      this.midTime = Math.floor(_duration/2);
      this.callback = _callback;
      this.changePerTick = (1/(_duration/2));
      this.alphaValue = 0;
      this.fadeColor = "#000";
      game.toggleGamePause(); //TODO remove this!! => best solution is to give the fader a start, midtime and end callback method
      
      this.update = function() {
        
        this.ticksSinceStart++;
        if (this.ticksSinceStart == this.duration) {
          game.toggleGamePause();//TODO: Remove this!!!
          return false;
        }
        
        if( this.midTime < this.ticksSinceStart ) {
          this.callback();
          this.midTime = this.duration;         
        }
        
        if( this.midTime != this.duration) {
           this.alphaValue += this.changePerTick;
        }
        else {
          this.alphaValue -= this.changePerTick;
        }
        
        return true;
        
      };
      
      this.draw = function() {
        var pAlpha = c.globalAlpha;
        
        c.fillStyle = this.fadeColor;
        c.globalAlpha = this.alphaValue;
        c.fillRect(0, 0, game.width, game.height);
        
        c.globalAlpha = pAlpha;
      };  
    };
    
  };

}

Game.INTRO_STATE = 0;
Game.INGAME_STATE = 1;
Game.OUTRO_STATE = 2;
Game.GAME_OVER = 3;